///////////////////////////////////////////////////////////////////////////////
//                   ALL STUDENTS COMPLETE THESE SECTIONS
// Title:            P1
// Files:            Symbol.java, SymbolTable.java, DuplicateException.java, EmptySymbolTableException.java
// Semester:         CS536 Spring 2013
//
// Author:           Sean Bauer, sbauer@cs.wisc.edu
// CS Login:         sbauer
// Lecturer's Name:  Hasti
//
///////////////////////////////////////////////////////////////////////////////
import java.lang.Math;

/**P1 is used to test the Symbol and SymbolTable classes.  Included amongst 
 * these tests are regular operation, boundary cases, as well as situations 
 * where exceptions should be thrown.
 * 
 * @author Sean Bauer
 */
public class P1 {
	// Control whether to output the print() method
	final static boolean PRINT_TABLES = true;
	
	// Define some arrays for testing input
	static String[] types = {
			"int",
			"double",
			"float",
			"long",
			"char",
			"bool",
			"String",
			"void"
	};
	static String[] names = {
			"1",
			"2",
			"3",
			"4",
			"5",
			"6",
			"7",
			"8"
	};

	/**
	 * Main method to conduct tests
	 * 
	 * @param args - not used
	 */
	public static void main(String[] args) {

		System.out.println("P1 running.");
		
		System.out.print("Testing Symbol class...");
		testSymbol();
		System.out.println("       done");
		
		System.out.print("Testing SymbolTable class...");
		testSymbolTable();
		System.out.println("  done");
		
		System.out.println("P1 completed.");
	}
	
	/**
	 * Function that encapsulates testing the Symbol class
	 */
	private static void testSymbol()
	{
		try {
			Symbol sym = new Symbol(null);
			System.out.print("\nSymbol was allowed with a null name");
		} catch(NullPointerException e) 
		{
			// Correct operation
		}
		
		// Try creating a bunch of normal symbols
		for (int i = 0; i < types.length; i++)
		{
			Symbol sym = new Symbol(types[i]);
			if (sym.getType() != types[i] || sym.toString() != types[i])
			{
				System.out.print("\nSymbol unsuccessfully created using " +
						"type = " + types[i]);
			}
		}
	}
	
	/**
	 * Function that encapsulates testing the SymbolTable class
	 */
	private static void testSymbolTable()
	{
		SymbolTable sym_table = new SymbolTable();
		
		// Fill a bunch of symbols
		Symbol[] test_symbols = new Symbol[types.length];
		// Protect against possible inconsistent lengths...
		int test_size = Math.min(names.length, test_symbols.length);
		for (int i = 0; i < test_size; i++) test_symbols[i] = new Symbol(types[i]);
		
		// Regular operation testing
		sym_table = new SymbolTable();
		try {
			if (PRINT_TABLES) sym_table.print();
			
			// Fill up symbol table
			for (int i = 0; i < test_size; i++)
			{
				sym_table.addMap();
				for (int j = 0; j <= i; j++)
				{
					sym_table.insert(names[j], test_symbols[j]);
				}
			}
			
			if (PRINT_TABLES) sym_table.print();
			
			// Global lookup
			for (int i = 0; i < test_size; i++)
			{
				if (sym_table.globalLookup(names[i]) != test_symbols[i]) 
				{
					System.out.print("\nGlobal lookup returned incorrect symbol.");
				}
			}
			
			// Local lookup tests
			for (int i = test_size-1; i >= 0; i--)
			{
				for (int j = 0; j <= i; j++)
				{
					if (j <= i) {
						if (sym_table.globalLookup(names[j]) != test_symbols[j]) 
						{
							System.out.print("\nLocal lookup returned " +
									"incorrect symbol.");
						}
					} else {
						if (sym_table.globalLookup(names[j]) != null) 
						{
							System.out.print("\nLocal lookup did not return " +
									"null during regular test.");
						}
					}
				}
				sym_table.removeMap();
			}
			
			if (PRINT_TABLES) sym_table.print();
			
		} catch (EmptySymbolTableException e) {
			System.out.print("\nEmptySymbolTableException thrown during " +
					"regular operation test.");
		} catch (DuplicateException e) {
			System.out.print("\nDuplicateException thrown during regular " +
					"operation test.");
		}
		
		
		
		// Lookup on empty table
		sym_table = new SymbolTable();
		try {
			sym_table.removeMap();
			Symbol result = sym_table.localLookup(names[0]);
			if (result != null) System.out.print("\nlocalLookup() returns " +
					"non-null result from empty table");
		} catch (EmptySymbolTableException e) {
			System.out.print("\nEmptySymbolTableException thrown when " +
					"calling localLookup() on empty table.");
		}
		
		// Remove on empty
		sym_table = new SymbolTable();
		try {
			sym_table.removeMap();
			sym_table.removeMap();
			System.out.print("\nEmptySymbolTableException not thrown when " +
					"calling removeMap() on empty table.");
		} catch (EmptySymbolTableException e) {
			// Correct operation
		}
		
		// Duplicate test
		sym_table = new SymbolTable();
		try {
			sym_table.insert(names[0], test_symbols[0]);
			sym_table.insert(names[1], test_symbols[1]);
			sym_table.insert(names[0], test_symbols[0]);
			System.out.print("\nDuplicateException not thrown when inserting " +
					"duplicates");
		} catch (EmptySymbolTableException e) {
			System.out.print("\nESTException incorrectly thrown when inserting " +
					"duplicates");
		} catch (DuplicateException e) {
			// Correct operation
		}
		
		// Try to insert with name=null
		sym_table = new SymbolTable();
		try {
			sym_table.insert(null, test_symbols[0]);
			System.out.print("\nNullPointerException not thrown when " +
					"inserting symbol with null name");
		} catch (EmptySymbolTableException e) {
			System.out.print("\nEmptySymbolTableException incorrectly " +
					"thrown when inserting symbol with null name");
		} catch (DuplicateException e) {
			System.out.print("\nDuplicateException incorrectly thrown " +
					"when inserting symbol with null name");
		} catch (NullPointerException e) {
			// Correct operation
		}
		
		// Try to insert into an empty symbol table
		sym_table = new SymbolTable();
		try {
			sym_table.removeMap();
			sym_table.insert(names[0], test_symbols[0]);
			System.out.print("\nEmptySymbolTableException not thrown " +
					"when calling insert() on empty table.");
		} catch (EmptySymbolTableException e) {
			// Correct operation
		} catch (DuplicateException e) {
			System.out.print("\nDuplicateException incorrectly thrown " +
					"when calling insert() on empty table.");
		}
		
	}

}
